/**
 *
 *  Copyright (c) 2011, AstroDAbis
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *      * Redistributions of source code must retain the above copyright notice,
 *        this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above copyright notice,
 *        this list of conditions and the following disclaimer in the documentation
 *        and/or other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 *  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 *  OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
package org.jiscinvolve.astrodabis.mallow.impl.model.api;

import java.util.List;
import java.util.Iterator;

import javax.persistence.Table;
import javax.persistence.Column;
import javax.persistence.Entity;
//import javax.persistence.Transient;

//import javax.persistence.OneToMany;
//import javax.persistence.JoinColumn;

//import javax.persistence.FetchType;
//import javax.persistence.CascadeType;

//import javax.persistence.Access;
//import javax.persistence.AccessType;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

//import org.springframework.beans.factory.annotation.Autowire;
//import org.springframework.beans.factory.annotation.Required;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.beans.factory.annotation.Configurable;

//import org.springframework.context.annotation.Scope;
//import org.springframework.context.annotation.ScopedProxyMode;

import org.springframework.transaction.annotation.Transactional; 

import org.jiscinvolve.astrodabis.mallow.model.api.Account;

import org.jiscinvolve.astrodabis.mallow.model.api.Tag;
import org.jiscinvolve.astrodabis.mallow.model.api.TagBean;
import org.jiscinvolve.astrodabis.mallow.model.api.TagManager;

import org.jiscinvolve.astrodabis.mallow.model.api.Query;
import org.jiscinvolve.astrodabis.mallow.model.api.QueryBean;
import org.jiscinvolve.astrodabis.mallow.model.api.QueryTag;
import org.jiscinvolve.astrodabis.mallow.model.api.QueryTagBean;
import org.jiscinvolve.astrodabis.mallow.model.api.QueryManager;

import org.jiscinvolve.astrodabis.mallow.model.api.JoinTag;
import org.jiscinvolve.astrodabis.mallow.model.api.JoinTagData;

import org.jiscinvolve.astrodabis.mallow.model.tap.TapTable;
import org.jiscinvolve.astrodabis.mallow.model.tap.TapTableBean;
import org.jiscinvolve.astrodabis.mallow.model.tap.TapTableManager;

import org.jiscinvolve.astrodabis.mallow.model.tap.TapService;
import org.jiscinvolve.astrodabis.mallow.model.tap.TapServiceBean;
import org.jiscinvolve.astrodabis.mallow.model.tap.TapServiceManager;

import org.jiscinvolve.astrodabis.mallow.model.jpa.DuplicateEntryException;

import org.jiscinvolve.astrodabis.mallow.impl.model.tap.TapTableImpl;
import org.jiscinvolve.astrodabis.mallow.impl.model.tap.TapServiceImpl;

import org.jiscinvolve.astrodabis.mallow.impl.model.jpa.ManagedObjectImpl;

/**
 * Hibernate implementation of the Account entity.
 *
 */
@Entity
@Table(name="accounts")
public class AccountImpl
extends ManagedObjectImpl
implements Account
    {

    /**
     * Our debug logger.
     * 
     */
    private static Log log = LogFactory.getLog(AccountImpl.class);

    /**
     * Protected constructor.
     *
     */
    protected AccountImpl()
        {
        super();
        }

    /**
     * Public constructor.
     *
     */
    public AccountImpl(String name, String email, String pass)
        {
        super();
        this.init(this);
        this.name  = name  ;
        this.email = email ;
        this.pass  = pass ;
        }

    /**
     * Reference to 'this' used by inner classes.
     *
     */
    protected AccountImpl self()
        {
        return this ;
        }

    /**
     * The Account name.
     *
     */
    @Column(name="name")
    private String name;

    @Override
    public String name()
        {
        return this.name ;
        }

    /**
     * The Account email address.
     *
     */
    @Column(name="email")
    private String email;

    @Override
    public String email()
        {
        return this.email ;
        }

    /**
     * The Account password.
     *
     */
    @Column(name="pass")
    private String pass;

    @Override
    public String pass()
        {
        return this.pass ;
        }

    /**
     * Implementation interface.
     *
     */
    public interface TagManagerEx
    extends TagManager
        {
        @Override
        public TagImpl create(TagBean bean)
        throws DuplicateEntryException;

        @Override
        public TagImpl name(String name);

        @Override
        public TagImpl name(String name, boolean create);
       
        }

    /**
     * Create a TagManager for this Account.
     *
     */
    public TagManagerEx tags()
        {
        return new TagManagerEx()
            {
            @Override
            @Transactional(readOnly = false)
            public TagImpl create(TagBean bean)
            throws DuplicateEntryException
                {
                return manager().tag(
                    self(),
                    bean
                    );
                }

            @Override
            @Transactional(readOnly = true)
            public Tag select(String ident)
                {
                return manager().tags().select(
                    ident
                    );
                }

            @Override
            @Transactional(readOnly = true)
            public Iterable<Tag> select()
                {
                return manager().tags(
                    self()
                    );
                }

            @Override
            @Transactional(readOnly = true)
            public TagImpl name(String name)
                {
                return name(
                    name,
                    false
                    );
                }

            @Override
            @Transactional(readOnly = true)
            public TagImpl name(String name, boolean create)
                {
// TODO Move into manager
                List<TagImpl> list = manager().template().find(
                    "FROM TagImpl WHERE owner = ? AND name = ?",
                    self(),
                    name
                    );  
                if (list.isEmpty())
                    {
                    if (create)
                        {
                        return manager().tag(
                            self(),
                            name
                            );
                        }
                    else {
                        return null ;
                        }
                    }
                else {
                    return list.get(0);
                    }
                }
            };
        }

    /**
     * Implementation interface.
     *
     */
    public interface QueryManagerEx
    extends QueryManager
        {

        @Override
        public QueryImpl create(Query.Action action, QueryBean bean);

        @Override
        public QueryImpl select(String ident);
        
        }

    /**
     * Create a QueryManager for this Account.
     *
     */
    public QueryManagerEx queries()
        {
        return new QueryManagerEx()
            {
            @Override
            @Transactional(readOnly = false)
            public QueryImpl create(Query.Action action, QueryBean bean)
                {
                return manager().query(
                    self(),
                    action,
                    bean
                    );
                }

            @Override
            @Transactional(readOnly = true)
            public QueryImpl select(String ident)
                {
//TODO add to manager implementation interface
                return manager().template().get(
                    QueryImpl.class,
                    ident
                    );  
                }

            @Override
            @Transactional(readOnly = true)
            public Iterable<Query> select()
                {
                return manager().queries(
                    self()
                    );
                }
            };
        }

    /**
     * Tag a query.
     *
     */
    public QueryTagImpl tag(Query query, QueryTagBean bean)
        {
        return manager().tag(
            self(),
            query,
            bean
            );
        }

// TapTables

    /**
     * Implementation interface.
     *
     */
    public interface TapTableManagerEx
    extends TapTableManager
        {
        @Override
        public TapTableImpl create(TapTable.Action action, TapTableBean bean);

        @Override
        public TapTableImpl select(String ident);
        
        }

    /**
     * Get a TapTableManager for this Account.
     *
     */
    public TapTableManagerEx tables()
        {
        return new TapTableManagerEx()
            {
// @todo Untangle the manager interfaces.
            @Override
            public TapTableImpl create(TapTable.Action action, TapTableBean bean)
                {
                return null ;
                }

            @Override
            @Transactional(readOnly = true)
            public TapTableImpl select(String ident)
                {
                return manager().table(
                    ident
                    );  
                }

            @Override
            public Iterable<TapTable> select()
                {
                return manager().tables(
                    self()
                    );
                }
            };
        }

// TapServices

    /**
     * Implementation interface.
     *
     */
    public interface TapServiceManagerEx
    extends TapServiceManager
        {
        @Override
        public TapServiceImpl create(TapService.Action action, TapServiceBean bean);

        @Override
        public TapServiceImpl select(String ident);
        
        }

    /**
     * Get a TapServiceManager for this Account.
     *
     */
    public TapServiceManagerEx services()
        {
        return new TapServiceManagerEx()
            {
            @Override
            public TapServiceImpl create(TapService.Action action, TapServiceBean bean)
                {
                return manager().service(
                    self(),
                    action,
                    bean
                    );
                }

            @Override
            @Transactional(readOnly = true)
            public TapServiceImpl select(String ident)
                {
                return manager().service(
                    ident
                    );  
                }

            @Override
            public Iterable<TapService> select()
                {
                return manager().services(
                    self()
                    );
                }
            };
        }

    /**
     * Tag a Join.
     *
     */
    public JoinTag tag(JoinTagData data)
        {
        log.debug("AccountImpl.tag(JoinTagData)");
        return manager().joins().tag(
            this,
            data
            );
        }

    /**
     * Tag a set of joins.
     *
    public Iterator<JoinTag> tag(final Iterator<JoinTagData> data)
        {
        return new Iterator<JoinTag>()
            {
            @Override
            public boolean hasNext()
                {
                return data.hasNext();
                }
            @Override
            public JoinTag next()
                {
                return manager().joins().tag(
                    self(),
                    data.next()
                    );
                }
            @Override
            public void remove()
                {
                throw new UnsupportedOperationException(
                    "Iterator.remove() not supported"
                    );
                }
            };
        }
     */

    }

